#include <stdio.h>
#include <stdlib.h>

double expon(double *, float);

#define NTYPE1 3			//The number of type1 customer per run
#define NTYPE2 3			//The number of type2 customer per run
#define EXPARRIV1 10		//The expected interarrival time of type1
#define EXPARRIV2 10			//The expected interarrival time of type2
#define EXPSERVICE 4		//The expected service time
#define INFINITE 100000000	//Indicate a very large value
#define IDLE 0
#define BUSY1 1
#define BUSY2 2

double seed = 1234567;

//define a structure for type1 customer
struct type1 {
	double atime;	//arrival time
};

//define a structure for type1 customer
struct type2 {
	double atime;	//arrival time
};

//Two type of customers
struct type1 high[NTYPE1];
struct type2 low[NTYPE2];

void init(double s, double a1, double a2)
{
	//Initialize the arrival time for all the customers
	int i,j;
	high[0].atime = expon(&s,a1);
	//printf("No. %d type1 customer has arrived at %f\r\n", 0, high[0].atime);
	low[0].atime = expon(&s,a2);
	//printf("No. %d type2 customer has arrived at %f\r\n", 0, low[0].atime);
	for(i=1; i<NTYPE1; i++)
	{
		high[i].atime = high[i-1].atime + expon(&s,a1);
		//printf("No. %d type1 customer has arrived at %f\r\n", i, high[i].atime);
	}
	for(j=1; j<NTYPE2; j++)
	{
		low[j].atime = low[j-1].atime + expon(&s,a2);
		//printf("No. %d type2 customer has arrived at %f\r\n", j, low[j].atime);
	}
}

double earliest(double a, double b, double c, double d)
{
	double min;
	if (a<b) min = a;
	else min = b;
	if (min>c) min = c;
	if (min>d) min = d;
	return min;
}

int main(void)
{
	int i = 0;					//index of type1 customer being served
	int j = 0;					//index of type2 customer being served or being preempted
	int ii = 0;					//index of type1 customer to arrive
	int jj = 0;					//index of type2 customer to arrive
	double clock = 0;			//record the current time
	double lastclock = 0;			//record the time of last event
	int queuelen1 = 0;			//length of queue of type1
	int queuelen2 = 0;			//length of queue of type2
	int servstatus = IDLE;		//server status is IDLE to begin with
	double sumnum1 = 0;			//sum of type1 number
	double sumnum2 = 0;			//sum of type2 number
	double sumwait1 = 0;		//sum of type1 waiting time
	double sumwait2 = 0;		//sum of type2 waiting time
	double sumserv1 = 0;		//sum of service time for type1
	double sumserv2 = 0;		//sum of service time for type2
	double interval = 0;		//time past before now and last event
	double nextarrive1;			//next type1 arrival time
	double nextdepart1;			//next type1 departure time
	double nextarrive2;			//next type2 arrival time
	double nextdepart2;			//next type2 departure time
	double lambda1;				//arrival rate of type1
	double lambda2;				//arrival rate of type2
	double mu;					//service rate
	double rho1;				//rho1 = lambda1/mu
	double rho2;				//rho2 = lambda2/mu

	init(seed, EXPARRIV1, EXPARRIV2);
	nextarrive1 = high[0].atime;	//next type1 arrival time
	nextdepart1 = INFINITE;		//next type1 departure time
	nextarrive2 = low[0].atime;	//next type2 arrival time
	nextdepart2 = INFINITE;		//next type2 departure time

	lambda1 = 1.0/EXPARRIV1;
	lambda2 = 1.0/EXPARRIV2;
	mu = 1.0/EXPSERVICE;
	rho1 = lambda1/mu;
	rho2 = lambda2/mu;

	while((i < NTYPE1)&&(j < NTYPE2))
	{
		lastclock = clock;				//record the time of last event
		/*******************************************
		Type 1 Arrival Event Handler
		********************************************/
		if (earliest(nextarrive1, nextdepart1, nextarrive2, nextdepart2) == nextarrive1)
		{
			clock = nextarrive1;
			interval = clock-lastclock;	//compute the time between last clock and current clock
			printf(" Time%5.1f: High %d arrives\r\n", clock, ii);
			ii ++;						//next type1 customer to arrive
			if(servstatus == IDLE)
			{
				servstatus = BUSY1;		//Change the server status into busy
				printf(" Time%5.1f: High %d starts service\r\n", clock, i);
				nextdepart1 = clock + expon(&seed,EXPSERVICE);
				sumnum1 += queuelen1 * interval;
				sumnum2 += queuelen2 * interval;
			}
			else if (servstatus == BUSY1)
			{
				sumnum1 += queuelen1 * interval;	//accumulate sumnum1 with queue length multiplied by the time the length holds
				sumnum2 += queuelen2 * interval;	//accumulate sumnum2 with queue length multiplied by the time the length holds
				queuelen1 = queuelen1 + 1.0;
				printf(" Time%5.1f: High %d gets in line and type1 line now has %d people\r\n", clock, ii-1, queuelen1);
				sumserv1 += interval;	//accumulate the total busy time of server serving type1
			}
			else
			{
				sumnum1 += queuelen1 * interval;
				sumnum2 += queuelen2 * interval;
				queuelen1 = queuelen1 + 1.0;
				printf(" Time%5.1f: High %d gets in line and type1 line now has %d people\r\n", clock, ii-1, queuelen1);
				sumserv2 += interval;		//accumulate the total busy time of server serving type2
			}
			if(ii<NTYPE1)	//more type1 customers to arrive
				nextarrive1 = high[ii].atime;
			else			//all type1 customers has arrived
				nextarrive1 = INFINITE;
		}

		/*******************************************
		Type 1 Departure Event Handler
		********************************************/
		if (earliest(nextarrive1, nextdepart1, nextarrive2, nextdepart2) == nextdepart1)
		{
			clock = nextdepart1;
			sumwait1 += clock - high[i].atime;	//add the waiting time (depart time - arrive time) for this type1 customer
			printf(" Time%5.1f: High %d departs\r\n", clock, i);
			i ++;	//move to next type1 customer
			interval = clock - lastclock;
			sumserv1 += interval;		//accumulate the total busy time of server serving type1
			if ((queuelen1 == 0) && (queuelen2 == 0))	//if no one is in queue
			{
				servstatus = IDLE;
				printf(" Time%5.1f: Server status starts to be IDLE\r\n", clock);
				nextdepart1 = INFINITE;
				nextdepart2 = INFINITE;
				sumnum1 += queuelen1 * interval;
				sumnum2 += queuelen2 * interval;
			}
			else if (queuelen1 != 0)				//if type1 queue is not empty
			{
				sumnum1 += queuelen1 * interval;	//accumulate sumnum1 with queue length multiplied by the time the length holds
				sumnum2 += queuelen2 * interval;	//accumulate sumnum2 with queue length multiplied by the time the length holds
				queuelen1 = queuelen1 - 1.0;
				servstatus = BUSY1;
				printf(" Time%5.1f: High %d leaves line to start service and type1 line now has %d people\r\n", clock, i, queuelen1);
				nextdepart1 = clock + expon(&seed,EXPSERVICE);
				nextdepart2 = INFINITE;
			}
			else									//if type1 queue is empty but type2 queue is not empty
			{
				sumnum1 += queuelen1 * interval;
				sumnum2 += queuelen2 * interval;	//accumulate sumnum2 with queue length multiplied by the time the length holds
				queuelen2 = queuelen2 - 1.0;
				servstatus = BUSY2;
				printf(" Time%5.1f: Low %d leaves line to start service and type2 line now has %d people\r\n", clock, j, queuelen2);
				nextdepart1 = INFINITE;
				nextdepart2 = clock + expon(&seed,EXPSERVICE);
			}
		}

		/*******************************************
		Type 2 Arrival Event Handler
		********************************************/
		if (earliest(nextarrive1, nextdepart1, nextarrive2, nextdepart2) == nextarrive2)
		{
			clock = nextarrive2;
			interval = clock-lastclock;		//compute the time between last clock and current clock
			printf(" Time%5.1f: Low %d arrives\r\n", clock, jj);
			jj ++;						//next type2 customer to arrive
			if(servstatus == IDLE)
			{
				servstatus = BUSY2; //Change the server status into busy
				printf(" Time%5.1f: Low %d starts to be served\r\n", clock, j);
				nextdepart2 = clock + expon(&seed,EXPSERVICE);
				sumnum1 += queuelen1 * interval;
				sumnum2 += queuelen2 * interval;
			}
			else if(servstatus == BUSY1)
			{

				sumnum1 += queuelen1 * interval;	//accumulate sumnum1 with queue length multiplied by the time the length holds
				sumnum2 += queuelen2 * interval;	//accumulate sumnum2 with queue length multiplied by the time the length holds
				queuelen2 = queuelen2 + 1.0;
				printf(" Time%5.1f: Low %d gets in line and type2 line now has %d people\r\n", clock, jj-1, queuelen2);
				sumserv1 += interval;	//accumulate the total busy time of server serving type1
			}
			else
			{
				sumnum1 += queuelen1 * interval;
				sumnum2 += queuelen2 * interval;	//accumulate sumnum2 with queue length multiplied by the time the length holds
				queuelen2 = queuelen2 + 1.0;
				printf(" Time%5.1f: Low %d gets in line and type2 line now has %d people\r\n", clock, jj-1, queuelen2);
				sumserv2 += interval;	//accumulate the total busy time of server serving type2
			}
			if(jj<NTYPE2)	//more type2 customers to arrive
				nextarrive2 = low[jj].atime;
			else			//all type2 customers has arrived
				nextarrive2 = INFINITE;
		}

		/*******************************************
		Type 2 Departure Event Handler
		********************************************/
		if (earliest(nextarrive1, nextdepart1, nextarrive2, nextdepart2) == nextdepart2)
		{
			clock = nextdepart2;
			sumwait2 += clock - low[j].atime;	//add the waiting time (depart time - arrive time) for this type2 customer
			printf(" Time%5.1f: Low %d departs\r\n", clock, j);
			j ++;	//move to next type2 customer
			interval = clock - lastclock;
			sumserv2 += interval;		//accumulate the total busy time of server serving type2
			nextdepart1 = INFINITE;
			if (queuelen1 != 0)			//if someone is in queue1
			{
				sumnum1 += queuelen1 * interval;
				sumnum2 += queuelen2 * interval;
				queuelen1 = queuelen1 - 1.0;
				servstatus = BUSY1;
				printf(" Time%5.1f: High %d starts service\r\n", clock, i);
				nextdepart1 = clock + expon(&seed,EXPSERVICE);
				nextdepart2 = INFINITE;
			}
			else if (queuelen2 == 0)			//if both queue1 and queue2 are empty
			{
				servstatus = IDLE;
				printf(" Time%5.1f: Server status starts to be IDLE\r\n", clock);
				nextdepart1 = INFINITE;
				nextdepart2 = INFINITE;
				sumnum1 += queuelen1 * interval;
				sumnum2 += queuelen2 * interval;
			}
			else								//if queue1 is empty but queue2 is not empty
			{
				sumnum1 += queuelen1 * interval;
				sumnum2 += queuelen2 * interval;	//accumulate sumnum2 with queue length multiplied by the time the length holds
				queuelen2 = queuelen2 - 1.0;
				servstatus = BUSY2;
				printf(" Time%5.1f: Low %d leaves line to start service and type2 line now has %d people\r\n", clock, j, queuelen2);
				nextdepart1 = INFINITE;
				nextdepart2 = clock + expon(&seed,EXPSERVICE);;		//predicts the departure time for type2 customer if not preempted again
			}
		}
	}

	/***********************************************
	Report the theoretical statistic results
	************************************************/
	//printf("The following is the theoretical results:\r\n");
	//printf("Check if %f < 1\r\n", rho1 + rho2);
	//printf("The avarage number of type1 customers in the system is %f\r\n", (rho1*(1+rho2))/(1-rho1));
	//printf("The avarage number of type2 customers in the system is %f\r\n", ((1-rho1*(1-rho1-rho2))*rho2)/((1-rho1)*(1-rho1-rho2)));
	//printf("The avarage time type1 customers spent in the system is %f\r\n", (1+rho2)/(mu*(1-rho1)));
	//printf("The avarage time type2 customers spent in the system is %f\r\n", ((1-rho1*(1-rho1-rho2)))/(mu*(1-rho1)*(1-rho1-rho2)));

	/***********************************************
	Report the statistic results
	************************************************/
	//printf("**************************************************\r\nThe following is the measured results:\r\n");
	//printf("The avarage number of type1 customers in the system is %f\r\n", sumnum1/clock);
	//printf("The avarage number of type2 customers in the system is %f\r\n", sumnum2/clock);
	//printf("The avarage time type1 customers spent in the system is %f\r\n", sumwait1/NTYPE1);
	//printf("The avarage time type2 customers spent in the system is %f\r\n", sumwait2/NTYPE2);
	//printf("The server utilization for type1 customer is %f\r\n", sumserv1/clock);
	//printf("The server utilization for type2 customer is %f\r\n", sumserv2/clock);
	getchar();
}